﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace SqlPrompt
{
    class CommandProcessor : IDisposable
    {
        const int MaxConnectionNameLength = 16;

        ConsoleDataTarget consoleDataTarget = new ConsoleDataTarget();
        DelimitedFileDataTarget delimitedFileDataTarget = new DelimitedFileDataTarget();
        IDataTarget currentDataTarget = null;
        QueryProcessor queryProcessor = new QueryProcessor();

        public CommandProcessor()
        {
			consoleDataTarget.ChunkedMode = true;

            currentDataTarget = consoleDataTarget;
        }

        public void Dispose()
        {
            queryProcessor.Disconnect();
        }

        public bool ProcessCommand(string cmd)
        {
			if (string.IsNullOrEmpty(cmd))
				return true;

            cmd = cmd.Trim();
            if (cmd[0] != '-')
            {
                queryProcessor.ProcessSqlCommand(cmd, currentDataTarget, false);
                return true;
            }

            string cmdName = "";
            for (int i = 0; i < cmd.Length; i++)
            {
                if (cmd[i] == ' ')
                    break;

                cmdName += cmd[i];
            }

            string parameters = "";
            if (cmdName.Length + 1 < cmd.Length)
                parameters = cmd.Substring(cmdName.Length + 1);

            string[] parts = cmd.Split(' ');

            if (cmdName == "-exit" || cmdName == "-e" || cmdName == "-quit" || cmdName == "-q")
                return false;
            else if (cmdName == "-help" || cmdName == "-h")
            {
                DisplayHelp();
            }
            else if (cmdName == "-connect" || cmdName == "-c")
            {
                DoCmdConnect(parameters);
            }
            else if (cmdName == "-disconnect" || cmdName == "-d")
            {
                queryProcessor.Disconnect();
            }
            else if (cmdName == "-newconnection" || cmdName == "-nc")
            {
                DoCmdNewConnection();
            }
			else if (cmdName == "-editconnection" || cmdName == "-ec")
			{
				DoCmdEditConnection(parameters);
			}
            else if (cmdName == "-deleteconnection" || cmdName == "-dc")
            {
                DoCmdDeleteConnection(parameters);
            }
            else if (cmdName == "-listconnections" || cmdName == "-lc")
            {
                DoCmdListConnections();
            }
            else if (cmdName == "-schema" || cmdName == "-sc")
            {
                DoCmdSchema(parameters);
            }
            else if (cmdName == "-settarget" || cmdName == "-st")
            {
                DoCmdSetTarget(parameters);
            }
            else if (cmdName == "-setoutputfilepath" || cmdName == "-sofp")
            {
                DoCmdSetOutputFilePath(parameters);
            }
			else if (cmdName == "-paste" || cmdName == "-p")
			{
				DoCmdPaste();
			}
			else if (cmdName == "-copyquery" || cmdName == "-cq")
			{
				DoCmdCopyQuery();
			}
            else App.Error("Unrecognised command: " + cmdName);

            return true;
        }

        void DisplayHelp()
        {
            ConsoleUtil.WriteResourceToConsole("help.txt");
        }

        void DoCmdConnect(string parameters)
        {
            if (string.IsNullOrEmpty(parameters))
            {
                App.Error("Please specify the name or number of the connection you wish to connect to.");
                return;
            }

            int index = GetConnectionIndex(parameters);
            if (index == -1)
            {
                App.Error("Connection '" + parameters + "' not found.");
                return;
            }

            queryProcessor.Connect(App.Config.DbConnections[index]);
        }

        void DoCmdNewConnection()
        {
			DbConnectionInfo conn = RequestConnectionInfo(null);
			if (conn == null) return;

            App.Config.DbConnections.Add(conn);
            App.Config.Save();

            App.Msg("Your new connection has been created. Type '-c " + conn.Name + "' to connect to it.");
        }

		void DoCmdEditConnection(string parameters)
		{
			int index = GetConnectionIndex(parameters);
			if (index == -1)
			{
				App.Error("Connection '" + parameters + "' not found.");
				return;
			}

			DbConnectionInfo conn = RequestConnectionInfo(App.Config.DbConnections[index]);
			if (conn == null) return;

			App.Config.DbConnections[index] = conn;
			App.Config.Save();

			App.Msg("Your connection has been edited. Type '-c " + conn.Name + "' to connect to it.");
		}

        void DoCmdDeleteConnection(string parameters)
        {
            if (string.IsNullOrEmpty(parameters))
            {
                App.Error("Please specify the name or number of the connection you wish to delete.");
                return;
            }

            int index = GetConnectionIndex(parameters);
            if (index == -1)
            {
                App.Error("Connection '" + parameters + "' not found.");
                return;
            }

            string name = App.Config.DbConnections[index].Name;

            App.Config.DbConnections.RemoveAt(index);
			App.Config.Save();
            App.Msg("Connection '" + name + "' removed.");
        }

        void DoCmdListConnections()
        {
            if (App.Config.DbConnections.Count == 0)
            {
                App.Msg("There are currently no saved connections");
                return;
            }

            App.ColumnHeading("Index", 5, true);
            App.ColumnHeading("Name", MaxConnectionNameLength, false);
            App.ColumnHeading("ADO Type", 8, false);
            App.ColumnHeading("Connection String", 50, false);
            App.NewLine();

            Console.ForegroundColor = ConsoleColor.Gray;
            for (int i = 0; i < App.Config.DbConnections.Count; i++)
            {
                DbConnectionInfo conn = App.Config.DbConnections[i];

                Console.Write((i + 1).ToString().PadRight(6));
                Console.Write(conn.Name.PadRight(MaxConnectionNameLength + 1));
                Console.Write(conn.AdoInterface.ToString().PadRight(9));
                Console.Write(conn.ConnectionString);
                App.NewLine();
            }
        }

        void DoCmdSchema(string parameters)
        {
            if (string.IsNullOrEmpty(parameters))
            {
                App.Error("Please enter a SQL command.");
                return;
            }

            queryProcessor.ProcessSqlCommand(parameters, consoleDataTarget, true);
        }

       void DoCmdSetTarget(string parameters)
        {
            string validOptionsMsg = "Valid options are 'console'/'con' and 'delimitedfile'/'dfile'.";
            if (string.IsNullOrEmpty(parameters))
            {
                App.Error("Please specify the target type. " + validOptionsMsg);
                return;
            }

            string target = parameters.Trim().ToLower();
            if (target == "console" || target == "con")
            {
                currentDataTarget = consoleDataTarget;
                App.Msg("Target set to console");
            }
            else if (target == "delimitedfile" || target == "dfile")
            {
                currentDataTarget = delimitedFileDataTarget;
                App.Msg("Target set to delimited file.");
            }
            else
            {
                App.Error("Unrecognised target '" + parameters + "'. " + validOptionsMsg);
            }
        }

        void DoCmdSetOutputFilePath(string parameters)
        {
            if (string.IsNullOrEmpty(parameters))
            {
                App.Error("Please specify a file path.");
                return;
            }

            parameters = parameters.Replace("\"", "");

            delimitedFileDataTarget.OutputFilePath = parameters;
            App.Msg("Output file path set to '" + parameters + "'.");
        }

		void DoCmdPaste()
		{
			if (!Clipboard.ContainsText())
			{
				App.Error("The clipboard does not contain text.");
				return;
			}

			string data = Clipboard.GetText();

			App.Msg("The follow data is present on your clipboard:");

			App.NewLine();
			Console.ForegroundColor = ConsoleColor.Gray;
			Console.WriteLine(StringUtil.RemoveTrailingCrLf(data));
			App.NewLine();

			if (!App.YesNo("Would you like to execute this as a command?")) return;
			App.NewLine();

			ProcessCommand(data);
		}

		void DoCmdCopyQuery()
		{
			Clipboard.SetText(queryProcessor.PreviousQuery);

			App.Msg("The last executed query has been copied to your clipboard.");
		}

        int GetConnectionIndex(string nameOrIndex)
        {
            int index = 0;
            if (int.TryParse(nameOrIndex, out index))
            {
                if (index > 0 && index <= App.Config.DbConnections.Count)
                    return index - 1;
            }

            return App.Config.GetConnectionIndexFromName(nameOrIndex);
        }

		DbConnectionInfo RequestConnectionInfo(DbConnectionInfo defaultValues)
        {
            DbConnectionInfo conn = new DbConnectionInfo();

			if (defaultValues != null)
				App.Msg("Press enter to leave value unchanged.");
			else
				defaultValues = new DbConnectionInfo();

            // Set the name.
			conn.Name = App.Prompt("Connection name", defaultValues.Name);
            if (string.IsNullOrEmpty(conn.Name))
            {
				App.Error("Please enter a valid name for your connection.");
				return null;
            }

            if (conn.Name.IndexOf(' ') != -1)
            {
                App.Error("The connection name must not contain spaces.");
                return null;
            }

            if (conn.Name.Length > MaxConnectionNameLength)
            {
                App.Error("The maximum length for a connection name is " + MaxConnectionNameLength + " characters.");
                return null;
            }
			
            // Set the ADO interface.
			string adoInterface = App.Prompt("ADO interface (SQL, ODBC, OLEDB)", defaultValues.AdoInterface.ToString());
            if (string.IsNullOrEmpty(adoInterface))
            {
				App.Error("Please specify the ADO interface type.");
				return null;
            }

            adoInterface = adoInterface.ToLower();
            if (adoInterface == "sql")
                conn.AdoInterface = AdoInterface.Sql;
            else if (adoInterface == "odbc")
                conn.AdoInterface = AdoInterface.Odbc;
            else if (adoInterface == "oledb")
                conn.AdoInterface = AdoInterface.OleDb;
            else
            {
                App.Error("Unrecognised ADO interface type. Accepted values are SQL, ODBC and OLEDB.");
                return null;
            }

            // Set the connection string
			conn.ConnectionString = App.Prompt("Connection string", defaultValues.ConnectionString);
            if (string.IsNullOrEmpty(conn.ConnectionString))
            {
				App.Error("Please enter a valid connection string your connection.");
				return null;
            }

            return conn;
        }
    }
}
